﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Data;
using System.Data.OleDb;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using Excel = Microsoft.Office.Interop.Excel;

namespace DataForest.DataModel
{
     public class Table : DataTable, INotifyPropertyChanged
    {
         

         private DataColumn targetAttribute;
         public DataColumn TargetAttribute
         {
             get
             {
                 return targetAttribute;
             }
             set
             {
                 if (targetAttribute != value)
                 {
                     targetAttribute = value;
                     OnPropertyChanged("TargetAttribute");
                 }
             }
         }

         private string fileName;
         public string FileName
         {
             get
             {
                 return fileName;
             }
             set
             {
                 if (fileName != value)
                 {
                     fileName = value;
                     if (String.IsNullOrEmpty(Name))
                     {
                         Name = Path.GetFileName(fileName);
                     }
                     OnPropertyChanged("FileName");
                 }
             }
         }

         private string usedSeperatorChar;
         public string UsedSeperatorChar
         {
             get
             {
                 return usedSeperatorChar;
             }
             set
             {

                 if (value != usedSeperatorChar)
                 {
                     usedSeperatorChar = value;
                     OnPropertyChanged("UsedSeperatorChar");
                 }
             }
         }

         private bool wasFirstRowColumnNames;
         public bool WasFirstRowColumnNames
         {
             get
             {
                 return wasFirstRowColumnNames;
             }
             set
             {
                 if (wasFirstRowColumnNames != value)
                 {
                     wasFirstRowColumnNames = value;
                     OnPropertyChanged("WasFirstRowColumnNames");
                 }
             }
         }



         private string name;
         public string Name
         {
             get
             {
                 return name;
             }
             set
             {
                 if (value != name)
                 {
                     name = value;
                     OnPropertyChanged("Name");
                     OnPropertyChanged("DisplayName");
                 }
             }
         }

         private bool hasChanged = false;
         public bool HasChanged
         {
             get
             {
                 return hasChanged;
             }
             protected set
             {
                 if (value != hasChanged)
                 {
                     hasChanged = value;
                     OnPropertyChanged("HasChanged");
                     OnPropertyChanged("DisplayName");
                 }
             }
         }

         public string DisplayName
         {
             get
             {
                 return HasChanged ? Name + "*" : Name;
             }
         }

         public Table()
         {
             this.RowChanged += HandleRowChanged;
         }

         private void HandleRowChanged(object sender, DataRowChangeEventArgs e)
         {
             HasChanged = true;
         }

         public bool IsDiscret(DataColumn col)
         {
             List<object> valueSet = this.AsEnumerable().Select(drow => drow[col]).Distinct().ToList();
             bool parsable = true;
             foreach (DataRow drow in this.Rows)
             {
                 if (drow[col] is string)
                 {
                     double number;
                     if (!Double.TryParse((string)drow[col], out number))
                     {
                         parsable = false;
                         break;
                     }
                 }
             }
             if (parsable && valueSet.Count > Node.MAX_DISCRETE_ATTRIBUTE_VALUES)
             {
                 return false;
             }

             return true;
         }


         //Quelle: http://stackoverflow.com/questions/1050112/how-to-read-a-csv-file-into-a-net-datatable 
         //answered Jun 3 '11 at 15:09 Nodir

         public static Table ReadCSV(string filename, string separatorChar, out List<string> errors)
         {
             #region Import Grid from CSV
             errors = new List<string>();
             var table = new Table();
             using (var sr = new StreamReader(filename, Encoding.Default))
             {
                 string line;
                 var i = 0;
                 while (sr.Peek() >= 0)
                 {
                     try
                     {
                         line = sr.ReadLine();
                         if (string.IsNullOrEmpty(line)) continue;
                         var values = line.Split(new[] { separatorChar }, StringSplitOptions.None);
                         var row = table.NewRow();
                         for (var colNum = 0; colNum < values.Length; colNum++)
                         {
                             var value = values[colNum];
                             if (i == 0)
                             {
                                 table.Columns.Add(value, typeof(String));
                             }
                             else
                             {
                                 row[table.Columns[colNum]] = value;
                             }
                         }
                         if (i != 0) table.Rows.Add(row);
                     }
                     catch (Exception ex)
                     {
                         errors.Add(ex.Message);
                     }
                     i++;
                 }
             }
             table.HasChanged = false;
             table.TargetAttribute = table.Columns[table.Columns.Count - 1];
             table.FileName = filename;
             table.UsedSeperatorChar = separatorChar;
             return table;
             
             #endregion
         }


         static public Table LoadExcel(string path, bool firstRowColumnNames)
         {
             Table table = new Table();
             // load excel, and create a new workbook
             Excel.Application excelApp = new Excel.Application();

             Excel.Application excel = new Excel.Application();
             Excel.Workbook wb = excel.Workbooks.Open(path);
             Excel.Worksheet sheet = wb.ActiveSheet;
             // column headings
             int columns = 0;
             while (sheet.Cells[1, columns+1].Value != null)
             {
                 if (firstRowColumnNames)
                 {
                     table.Columns.Add(sheet.Cells[1, columns + 1].Value, typeof(string));
                 }
                 else
                 {
                     table.Columns.Add("Spalte " + columns, typeof(string));
                 }
                 columns++;
             }

             int row = 2;
             if (!firstRowColumnNames) { row = 1; }

             while (sheet.Cells[row, 1].Value != null) {
                 object[] rowData = new object[columns];
                 for (int i = 0; i < columns; i++)
                 {
                     if (sheet.Cells[row, i + 1].Value != null)
                     {
                         rowData[i] = sheet.Cells[row, i + 1].Value;
                     }
                     else
                     {
                         rowData[i] = null;
                     }
                 }
                 table.Rows.Add(rowData);
                 row++;
             }
             table.FileName = path;
             table.wasFirstRowColumnNames = firstRowColumnNames;
             table.HasChanged = false;
             table.TargetAttribute = table.Columns[table.Columns.Count - 1];
             return table;
         }

         static public Table LoadXml(string path)
         {
             Table table = new Table();
             table.ReadXml(path);
             table.FileName = path;
             table.HasChanged = false;
             table.TargetAttribute = table.Columns[table.Columns.Count - 1];
             return table;
         }

         public void CreateExcel(string path, bool firstRowColumnNames)
         {
            
            // load excel, and create a new workbook
            Excel.Application excelApp = new Excel.Application();
            excelApp.Workbooks.Add();

            // single worksheet
            Excel._Worksheet workSheet = excelApp.ActiveSheet;

            // column headings
            if (firstRowColumnNames)
            {
                for (int i = 0; i < this.Columns.Count; i++)
                {
                    workSheet.Cells[1, (i + 1)] = this.Columns[i].ColumnName;
                }
            }

            // rows
            int offset = 1;
            if (firstRowColumnNames) { offset = 2; }
            for (int i = 0; i < this.Rows.Count; i++)
            {
                // to do: format datetime values before printing
                for (int j = 0; j < this.Columns.Count; j++)
                {
                    workSheet.Cells[(i + offset), (j + 1)] = this.Rows[i][j];
                }
            }

           
            workSheet.SaveAs(path);
            excelApp.Quit();
            
            this.HasChanged = false;
         }

         public void CreateXml(string path)
         {
             this.WriteXml(path);
             this.HasChanged = false;
         }

         // Quelle http://stackoverflow.com/questions/888181/convert-datatable-to-csv-stream
         // answered May 20 '09 at 15:46 adopilot

         public void CreateCSVFile(string strFilePath, string separator)
         {
             #region Export Grid to CSV
             // Create the CSV file to which grid data will be exported.
             StreamWriter sw = new StreamWriter(strFilePath, false);
             int iColCount = this.Columns.Count;

             // First we will write the headers.

             //DataTable dt = m_dsProducts.Tables[0];
             for (int i = 0; i < iColCount; i++)
             {
                 sw.Write(this.Columns[i]);
                 if (i < iColCount - 1)
                 {
                     sw.Write(separator);
                 }
             }
             sw.Write(sw.NewLine);

             // Now write all the rows.
             foreach (DataRow dr in this.Rows)
             {
                 for (int i = 0; i < iColCount; i++)
                 {
                     if (!Convert.IsDBNull(dr[i]))
                     {
                         sw.Write(dr[i].ToString());
                     }
                     if (i < iColCount - 1)
                     {
                         sw.Write(separator);
                     }
                 }
                 sw.Write(sw.NewLine);
             }
             sw.Close();
             this.HasChanged = false;
             #endregion
         }


         public void OnColumnChanged(DataColumnChangeEventArgs e)
         {
            //Letzte Spalte als Zielattribut setzen, falls dieses NULL ist
             if (this.TargetAttribute == null)
             {
                 this.TargetAttribute = this.Columns[this.Columns.Count-1];
             }
         }

         public event PropertyChangedEventHandler PropertyChanged;

         protected void OnPropertyChanged(string property)
         {
             PropertyChangedEventHandler handler = PropertyChanged;
             if (handler != null)
             {
                 handler(this, new PropertyChangedEventArgs(property));
             }
         }

    }
}
